package analysis

import (
	"github.com/shopspring/decimal"
	"market/internal"
	"time"
)

type Strategy interface {
	IsBuy(avgBuySize, avgSellSize, longBuySize, longSellSize float64) bool
	IsSell(avgBuySize, avgSellSize, longBuySize, longSellSize float64) bool
}

type TimesStrategy struct {
	Buy  *TimesOption
	Sell *TimesOption
}

func (t *TimesStrategy) IsBuy(avgBuySize, avgSellSize, longBuySize, longSellSize float64) bool {
	return t.Buy.isOK(avgBuySize, avgSellSize, longBuySize, longSellSize)
}

func (t *TimesStrategy) IsSell(avgBuySize, avgSellSize, longBuySize, longSellSize float64) bool {
	return t.Sell.isOK(avgSellSize, avgBuySize, longSellSize, longBuySize)
}

type TimesOption struct {
	Times float64 // 倍数
	Lasts int     // 持续次数
	lasts int     // 已持续次数
}

func (to *TimesOption) isOK(avgBig, avgSmall, longBig, longSmall float64) bool {
	if avgBig > longBig && avgBig/avgSmall >= to.Times {
		to.lasts++
		if to.lasts >= to.Lasts {
			to.lasts = 0
			return true
		}
	} else {
		to.lasts = 0
	}
	return false
}

type Player struct {
	usdt             decimal.Decimal
	eachOrderUseUSDT decimal.Decimal
	coins            decimal.Decimal
	order            *PlayerOrder
	orders           []*PlayerOrder
	flow             *internal.Flow
	strategy         Strategy
}

func NewPlayer(strategy Strategy, usdt decimal.Decimal) *Player {
	return &Player{
		usdt:             usdt,
		eachOrderUseUSDT: decimal.New(100, 0),
		orders:           nil,
		strategy:         strategy,
	}
}

type PlayerOrder struct {
	Buy      *Order
	Sell     *Order
	EarnUSDT decimal.Decimal
}

type Order struct {
	Size decimal.Decimal
	USDT decimal.Decimal
	Time time.Time
	Flow *internal.Flow
}

func (p *Player) GetTotalUSDT() decimal.Decimal {
	total := p.usdt
	price, _ := p.flow.GetSellCoinPrice()
	usdt := p.coins.Mul(price)
	total = total.Add(usdt)
	return total
}

func (p *Player) LenOrders() int {
	return len(p.orders)
}

func (p *Player) GetOrders() []*PlayerOrder {
	return p.orders
}

func (p *Player) Buy(flow *internal.Flow) {
	price, _ := flow.GetBuyCoinPrice()
	coins := p.eachOrderUseUSDT.Div(price)
	p.usdt = p.usdt.Sub(p.eachOrderUseUSDT)
	p.coins = p.coins.Add(coins)
	p.order = &PlayerOrder{
		Buy: &Order{
			Size: coins,
			USDT: p.eachOrderUseUSDT,
			Time: time.Unix(flow.Timestamp, 0),
			Flow: flow,
		},
		Sell:     nil,
		EarnUSDT: decimal.Decimal{},
	}
}

func (p *Player) Sell(flow *internal.Flow) {
	price, _ := flow.GetSellCoinPrice()
	usdt := p.coins.Mul(price)
	p.usdt = p.usdt.Add(usdt)
	p.order.Sell = &Order{
		Size: p.coins,
		USDT: usdt,
		Time: time.Unix(flow.Timestamp, 0),
		Flow: flow,
	}
	p.order.EarnUSDT = usdt.Sub(p.order.Buy.USDT)
	p.orders = append(p.orders, p.order)
	p.order = nil
	p.coins = decimal.Decimal{}
}

func (p *Player) Play(flow *internal.Flow, avgBuySize, avgSellSize, longBuySize, longSellSize float64) {
	p.flow = flow
	if p.order == nil {
		if p.strategy.IsBuy(avgBuySize, avgSellSize, longBuySize, longSellSize) {
			p.Buy(flow)
		}
	} else {
		if p.strategy.IsSell(avgBuySize, avgSellSize, longBuySize, longSellSize) {
			p.Sell(flow)
		}
	}
}
